package com.yide.myworld.utils;

import com.yide.myworld.exception.Asserts;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class DateUtil {

    public static final String yyyy__MM__dd = "yyyy/MM/dd";
    public static final String MM_dd = "MM月dd日";
    public static final String yyyy_MM_dd = "yyyy-MM-dd";
    public static final String yyyy_MM = "yyyy-MM";
    public static final String HH_mm_ss = "HH:mm:ss";
    public static final String yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm";
    public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
    public static final String yyyy_MM_ddTHH_mm_ss_XXX = "yyyy-MM-dd'T'HH:mm:ssXXX";
    public static final String yyyyMMdd = "yyyyMMdd";
    public static final String yyyyMMddHHmmss = "yyyyMMddHHmmss";
    public static final String yyMMddHHmmssfff = "yyMMddHHmmssSSS";
    public static final String yyyy_MM_dd_HH_mm_ss_fff = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String yyyy_MM_dd_HH_mm_ss_fffffff = "yyyy-MM-dd HH:mm:ss.SSSSSSS";
    private static long nd = 1000 * 24 * 60 * 60;//每天毫秒数
    private static long nh = 1000 * 60 * 60;//每小时毫秒数
    private static long nm = 1000 * 60;//每分钟毫秒数

    public static final Date DEFAULT_DATE = parseDate("1970-01-01");

    private final static TimeZone timeZone = TimeZone.getTimeZone("GMT+08:00");

    private final static SimpleDateFormat shortSdf = new SimpleDateFormat("yyyy-MM-dd");
    private final static SimpleDateFormat longSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 获取日期指定格式的格式化字符串
     *
     * @param date    待格式化日期
     * @param pattern 格式模式，可选DateUtil的公开静态常量定义
     * @return
     */
    public static String formatDate(Date date, String pattern) {
        try {
            return getDateFormat(pattern).format(date);
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 获取日期默认的格式化字符串(默认格式:yyyy-MM-dd)
     *
     * @param date 待格式化日期
     * @return
     */
    public static String formatDate(Date date) {
        return formatDate(date, yyyy_MM_dd);
    }

    /**
     * 获得本周的第一天，周一
     *
     * @return
     */
    public static Date getCurrentWeekDayStartTime() {
        Calendar c = Calendar.getInstance();
        try {
            int weekday = c.get(Calendar.DAY_OF_WEEK) - 2;
            c.add(Calendar.DATE, -weekday);
            c.setTime(longSdf.parse(shortSdf.format(c.getTime()) + " 00:00:00"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c.getTime();
    }


    /**
     * 获得本周的最后一天，周日
     *
     * @return
     */
    public static Date getCurrentWeekDayEndTime() {
        Calendar c = Calendar.getInstance();
        try {
            int weekday = c.get(Calendar.DAY_OF_WEEK);
            c.add(Calendar.DATE, 8 - weekday);
            c.setTime(longSdf.parse(shortSdf.format(c.getTime()) + " 23:59:59"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c.getTime();
    }

    /**
     * 根据指定的格式将日期字符串转换成日期对象
     *
     * @param dateStr 待转换字符串
     * @param pattern 格式模式，可选DateUtil的公开静态定义
     * @return
     * @throws ParseException
     */
    public static Date parseDate(String dateStr, String pattern) {
        try {
            return getDateFormat(pattern).parse(dateStr);
        } catch (ParseException ex) {
            return null;
        }
    }

    /**
     * 将日期字符串转换成日期对象(默认格式:yyyy-MM-dd)
     *
     * @param dateStr 待转换字符串
     * @return
     * @throws ParseException
     */
    public static Date parseDate(String dateStr) {
        return parseDate(dateStr, yyyy_MM_dd);
    }

    /**
     * 根据生日判断是否为成年人
     *
     * @param birthday
     * @return
     */
    public static boolean isAdult(Date birthday) {
        Calendar calendar = Calendar.getInstance(timeZone);
        calendar.add(Calendar.YEAR, -18);
        return calendar.getTime().after(birthday);
    }

    private static DateFormat getDateFormat(String pattern) {
        return new SimpleDateFormat(pattern);
    }


    /**
     * 通过时间秒毫秒数判断两个时间间隔天数（满24小时进1天）
     *
     * @param date1 被减时间
     * @param date2
     * @return
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        return (int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24));
    }

    /**
     * 获取指定时间的小时
     *
     * @param date
     * @return
     */
    public static int getHours(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }

    /**
     * 获取指定时间的分钟
     *
     * @param date
     * @return
     */
    public static int getMinutes(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MINUTE);
    }

    /**
     * 返回日期加X天后的日期
     *
     * @param date
     * @param i
     * @return
     */
    public static Date addDay(Date date, int i) {
        try {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(GregorianCalendar.DATE, i);
            return calendar.getTime();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 返回日期加X分钟后后的日期
     *
     * @param date
     * @param i
     * @return
     */
    public static Date addMinute(Date date, int i) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(GregorianCalendar.MINUTE, i);
        return calendar.getTime();
    }

    /**
     * 获取指定日期的日期部分
     *
     * @param date
     * @return
     */
    public static Date getDatePart(Date date) {
        return DateUtil.parseDate(DateUtil.formatDate(date, DateUtil.yyyy_MM_dd), DateUtil.yyyy_MM_dd);
    }

    /**
     * 获取指定日期的时间部分
     *
     * @param date
     * @return
     */
    public static String getTimePart(Date date) {
        return DateUtil.formatDate(date, DateUtil.HH_mm_ss);
    }

    /**
     * 指定字符串转换成标准时间部分(HH:mm:ss格式)
     *
     * @param str
     * @return
     */
    public static String convertTimePart(String str) {
        if (StringUtils.isEmpty(str)) return "";
        String[] array = str.split(":");
        if (array.length == 1) {
            return (array[0].length() == 1 ? "0" + array[0] : array[0]) + ":00:00";
        }
        if (array.length == 2) {
            return (array[0].length() == 1 ? "0" + array[0] : array[0]) + ":" +
                 (array[1].length() == 1 ? "0" + array[1] : array[1]) + ":00";
        }
        if (array.length == 3) {
            return DateUtil.formatDate(DateUtil.parseDate(str, DateUtil.HH_mm_ss), DateUtil.HH_mm_ss);
        }
        return str;
    }


    /**
     * 传入时间是否超过当前时间指定的差值
     *
     * @param date
     * @param diff
     * @return
     */

    public static boolean isDateThanNowDateOverDiff(long date, Integer diff) {
        long nm = 1000 * 60;
        int minNumber = (int) ((System.currentTimeMillis() - date) / nm);
        return minNumber >= diff;
    }


    /**
     * 日期比大小
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int dateCompare(String date1, String date2) {
        Date date_1 = parseDate(date1, yyyy_MM_dd_HH_mm_ss);
        Date date_2 = parseDate(date2, yyyy_MM_dd_HH_mm_ss);
        if (date_1.after(date_2)) {
            return 1;
        }
        if (date_1.before(date_2)) {
            return -1;
        }
        return 0;
    }

    /**
     * 根据日期时间字符串获取日期时间
     *
     * @param datetime
     * @param format   为null或者空时默认为yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static LocalDateTime stringToDateTime(String datetime, String format) {

        if (StringUtils.isEmpty(format)) {
            format = "yyyy-MM-dd HH:mm:ss";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(datetime, formatter);
    }

    public static LocalDateTime parseStringToDateTime(String times) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.parse(times, df);
        return ldt;
    }

    /**
     * (方法说明描述) 当前时间是几点
     *
     * @return
     */
    public static int getHourOfDay() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        int hour = cal.get(Calendar.HOUR_OF_DAY);
        return hour;
    }


    /**
     * 计算俩时间相差分钟数
     *
     * @param endDate   结束时间
     * @param startDate 开始时间
     * @return
     */
    public static int getDiffMinutes(Date endDate, Date startDate) {
        return (int) ((endDate.getTime() - startDate.getTime()) / (1000 * 60));
    }

    /**
     * 计算俩时间相差秒数
     *
     * @param endDate   结束时间
     * @param startDate 开始时间
     * @return
     */
    public static int getDiffSeconds(Date endDate, Date startDate) {
        return (int) ((endDate.getTime() - startDate.getTime()) / 1000);
    }

    public static String getCurrentDateTimeString(String pattern) {
        Date currentDate = new Date();
        String formatDate = formatDate(currentDate, pattern);
        return formatDate;
    }

    /**
     * 获取当天开始时间(00:00:00)
     *
     * @return
     */
    public static Date getCurrentDayStartTime() {
        return new Date(LocalDateTime.of(LocalDate.now(), LocalTime.MIN).toInstant(ZoneOffset.of("+8")).toEpochMilli());
    }

    /**
     * 获取当天结束时间(23:59:59)
     *
     * @return
     */
    public static Date getCurrentDayEndTime() {
        return new Date(LocalDateTime.of(LocalDate.now(), LocalTime.MAX).toInstant(ZoneOffset.of("+8")).toEpochMilli());
    }

    /**
     * 获取昨天的开始时间（昨天的00:00:00）
     *
     * @return
     */
    public static Date getYesterdayStartTime() {
        Date currentDayStartTime = getCurrentDayStartTime();
        return addDay(currentDayStartTime, -1);
    }

    /**
     * 获取昨天的结束时间（昨天的23:59:59）
     *
     * @return
     */
    public static Date getYesterdayEndTime() {
        Date currentDayStartTime = getCurrentDayEndTime();
        return addDay(currentDayStartTime, -1);
    }


    /**
     * 将长时间格式字符串转换为时间 yyyy-MM-dd HH:mm:ss
     *
     * @param strDate
     * @return
     */
    public static Date strToDateLong(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }


    /**
     * 根据日期获得该日期所在星期的星期一
     *
     * @return
     * @throws ParseException
     */
    public static Date getThisMondayByTime(Date date) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        //1--7的值,对应：星期日，星期一，星期二，星期三....星期六
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        //如果是【星期日】，则往前退6天
        if (dayOfWeek == 1) {
            calendar.add(Calendar.DAY_OF_MONTH, -6);
        } else if (dayOfWeek == 2) {
            //如果是【星期一】则不退
        } else {
            //【其他星期几】则退dayOfWeek-2天
            calendar.add(Calendar.DAY_OF_MONTH, -(dayOfWeek - 2));
        }
        Date time = calendar.getTime();
        return time;
    }

    /**
     * 根据日期获得该日期上一周的周一
     *
     * @return
     * @throws ParseException
     */
    public static Date getLastMondayByTime(Date date) throws ParseException {
        //先找到当前日期的周一，然后在这个基础上往前退7天
        return addDay(getThisMondayByTime(date), -7);
    }

    /**
     * 据日期获得该日期上一周的周日
     *
     * @param date
     * @return
     * @throws ParseException
     */
    public static Date getLastSundayByTime(Date date) throws ParseException {
        //先找到当前日期的周一，然后在这个基础上往前退7天
        return addDay(getThisMondayByTime(date), -1);
    }

    /**
     * 获得某个日期的本周的第一天，周一
     *
     * @return
     */
    public static Date getWeekDayStartTime(Date startDate) {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat shortSdf = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat longSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        c.setTime(new Date(startDate.getTime() - 1000 * 60 * 60 * 24));
        try {
            int weekday = c.get(Calendar.DAY_OF_WEEK) - 2;
            c.add(Calendar.DATE, -weekday);
            c.setTime(longSdf.parse(shortSdf.format(c.getTime()) + " 00:00:00"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c.getTime();
    }

    /**
     * 获得某个日期的本周的最后一天，周日
     *
     * @return
     */
    public static Date getWeekDayEndTime(Date endDate) {
        Calendar c = Calendar.getInstance();
        SimpleDateFormat shortSdf = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat longSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        c.setTime(new Date(endDate.getTime() - 1000 * 60 * 60 * 24));
        try {
            int weekday = c.get(Calendar.DAY_OF_WEEK);
            c.add(Calendar.DATE, 8 - weekday);
            c.setTime(longSdf.parse(shortSdf.format(c.getTime()) + " 23:59:59"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c.getTime();
    }

    public static Date getLastWeekMonday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getWeekDayStartTime(date));
        cal.add(Calendar.DATE, -7);
        return cal.getTime();
    }

    public static Date getLastWeekSunday(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getWeekDayEndTime(date));
        cal.add(Calendar.DATE, -7);
        return cal.getTime();
    }

    /**
     * 获取当前月第一天
     *
     * @param now
     * @return
     */
    public static Date getFirstDayOfMonth(Date now) {
        Calendar c = Calendar.getInstance();
        c.setTime(now);
        //设置为1号,当前日期既为本月第一天
        c.set(Calendar.DAY_OF_MONTH, 1);
        //将小时至0
        c.set(Calendar.HOUR_OF_DAY, 0);
        //将分钟至0
        c.set(Calendar.MINUTE, 0);
        //将秒至0
        c.set(Calendar.SECOND, 0);
        //将毫秒至0
        c.set(Calendar.MILLISECOND, 0);
        return new Date(c.getTimeInMillis());
    }

    /**
     * 根据类型 返回对应的开始时间和结束时间
     *
     * @param dateType
     * @return
     */
    public static Map<String,LocalDate> getDateByType(Integer dateType) {

        //开始时间
        LocalDate startDate = null;
        //结束时间
        LocalDate endDate = null;
        //根据当前时间进行计算
        LocalDate today = LocalDate.now();

        Map<String,LocalDate> localDateMap = new HashMap<>();

        switch (dateType) {
            case 1:
                // 近7天
                startDate = today.minusDays(6);
                endDate = today;
                break;
            case 2:
                // 近30天
                startDate = today.minusDays(29);
                endDate = today;
                break;
            case 3:
                // 近1年
                startDate = today.minus(1, ChronoUnit.YEARS);
                endDate = today;
                break;
        }

        if(startDate == null || endDate == null) {
            Asserts.fail("参数错误，获取开始或结束时间段失败");
        }

        localDateMap.put("startDate",startDate);
        localDateMap.put("endDate",endDate);

        return localDateMap;
    }

    /**
     * 获取指定时间区间的所有数据（包含日期和月份）
     *
     * @param dBegin
     * @param dEnd
     * @param rule   日历规则 如：Calendar.DAY_OF_MONTH 年月日
     * @return
     */
    public static List<Date> findDates(Date dBegin, Date dEnd, int rule) {
        List lDate = new ArrayList();
        if (dEnd.before(dBegin)) {
            return lDate;
        }
        lDate.add(dBegin);
        Calendar calBegin = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calBegin.setTime(dBegin);
        Calendar calEnd = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        calEnd.setTime(dEnd);
        // 测试此日期是否在指定日期之后
        while (dEnd.after(calBegin.getTime())) {
            // 根据日历的规则，为给定的日历字段添加或减去指定的时间量
            calBegin.add(rule, 1);
            lDate.add(calBegin.getTime());
        }
        return lDate;
    }

    public static Boolean isSameMonth(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
    }

    public static Boolean isItWithinTheTimeFrame(Date date1, Date date2, Date checkDate) {
        if (ObjectUtils.isEmpty(date1) && ObjectUtils.isEmpty(date2)) {
            return false;
        }
        long now = ObjectUtils.isEmpty(checkDate) ? System.currentTimeMillis() : checkDate.getTime();
        if (ObjectUtils.isEmpty(date1)) {
            return now <= date2.getTime();
        }
        if (ObjectUtils.isEmpty(date2)) {
            return now >= date1.getTime();
        }
        return now >= date1.getTime() && now <= date2.getTime();
    }

    public static void main(String[] args) throws ParseException {
        System.out.println(DateUtil.getSecondNumber());
//
//        ArrayList<Integer> integers = new ArrayList<>();
//        for (int i = 0; i < 10000; i++) {
//            integers.add(i);
//        }
//        integers.parallelStream().forEach(i -> {
//            Date lastWeekMonday = getWeekDayStartTime(new Date());
//            Date lastWeekSunday = getWeekDayEndTime(new Date());
//            System.out.println("lastWeekMonday:" + new Timestamp(lastWeekMonday.getTime()));
//            System.out.println("lastWeekSunday:" + new Timestamp(lastWeekSunday.getTime()));
//        });

    }

    public static long getSecondNumber() {
        LocalTime midnight = LocalTime.MIDNIGHT;
        LocalDate today = LocalDate.now();
        LocalDateTime todayMidnight = LocalDateTime.of( today, midnight );
        LocalDateTime tomorrowMidnight = todayMidnight.plusDays( 1 );
        long seconds = TimeUnit.NANOSECONDS.toSeconds( Duration.between( LocalDateTime.now(), tomorrowMidnight ).toNanos() );
        return seconds;
    }

}

